{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# TKinter项目实战-屏保\n",
    "### 项目分析\n",
    "- 屏保可以自己启动，也可以手动启动\n",
    "- 一旦敲击键盘或者移动鼠标后，或者其他的引发时间，则停止\n",
    "- 如果屏保是一幅画的话，则没有画框\n",
    "- 图像的动作是随机的，具有随机性，可能包括颜色，大小，多少， 运动方向，变形等\n",
    "- 整个世界的构成是：\n",
    "    - ScreenSaver:\n",
    "        - 需要一个canvas， 大小与屏幕一致，没有边框\n",
    "       \n",
    "    - Ball\n",
    "        - 颜色，大小，多少， 运动方向，变形等随机\n",
    "        - 球能动，可以被调用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "ename": "SyntaxError",
     "evalue": "invalid syntax (<ipython-input-6-9882ba1febcd>, line 8)",
     "output_type": "error",
     "traceback": [
      "\u001b[0;36m  File \u001b[0;32m\"<ipython-input-6-9882ba1febcd>\"\u001b[0;36m, line \u001b[0;32m8\u001b[0m\n\u001b[0;31m    def init\u001b[0m\n\u001b[0m            ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n"
     ]
    }
   ],
   "source": [
    "import random\n",
    "import tkinter\n",
    "\n",
    "class RandomBall(): \n",
    "    '''\n",
    "    定义运动的球的类\n",
    "    '''\n",
    "    def __init__(self, canvas, scrnwidth, scrnheight)\n",
    "    '''\n",
    "    canvas: 画布，所有的内容都应该在画布上呈现出来，此处通过此变量传入\n",
    "    scrnwidth/scrnheigh:屏幕宽高\n",
    "    '''\n",
    "    # 球出现的初始位置要随机，此处位置表示的球的圆心\n",
    "    # xpos表示位置的x坐标\n",
    "    self.xpos = random.randint(10， int(scrnwidth)-20)\n",
    "    # ypos表示位置的y坐标\n",
    "    \n",
    "    \n",
    "    #定义球运动的速度\n",
    "    # 模拟运动：不断的擦掉原来画，然后在一个新的地方再从新绘制\n",
    "    # 此处xvelocity模拟x轴方向运动\n",
    "    self.xvelocity = random.randint(4, 20)\n",
    "    # 同理，yvelocity模拟的是y轴方向运动\n",
    "    \n",
    "    \n",
    "    #定义屏幕的大小\n",
    "    self.scrnwidth = scrnwidth\n",
    "    # 定义屏幕的高度\n",
    "    \n",
    "    \n",
    "    # 球的大小随机\n",
    "    # 此处球的大小用半径表示\n",
    "    self.radius = random.randint(20, 120)\n",
    "    \n",
    "    # 定义颜色\n",
    "    # RGB表示法：三个数字，每个数字的值是0-255之间，表示红绿蓝三个颜色的大小\n",
    "    # 在某些系统中，之间用英文单词表示也可以，比如red, green\n",
    "    # 此处用lambda表达式\n",
    "    c = lambda: random.randint(0,255)\n",
    "    self.color ='#%02x%02x%02x'%(c(), c(), c()) \n",
    "    \n",
    "    # 以上函数代码忘了缩进了，自行处理\n",
    "\n",
    "        \n",
    "    def create_ball(self:\n",
    "        '''\n",
    "        用构造函数定义的变量值，在canvas上画一个球\n",
    "        '''\n",
    "        #tkinter没有画圆形函数\n",
    "        # 只有一个画椭圆函数，画椭圆需要定义两个坐标，\n",
    "        # 在一个长方形内画椭圆，我们只需要定义长方形左上角和右下角就好\n",
    "        # 求两个坐标的方法是，已知圆心的坐标，则圆心坐标减去半径能求出\n",
    "        #左上角坐标，加上半径能求出右下角坐标\n",
    "        x1 = self.xpos - self.radius\n",
    "        # 继续球y1, x2, y2\n",
    "    \n",
    "        # 再有两个对角坐标的前提下，可以进行画圆\n",
    "        # fill表示填充颜色\n",
    "        # outline是外围边框颜色\n",
    "        self.item = self.canvas.create_oval(x1, y1, x2, y2, \\\n",
    "                                           fill=self.color, \\\n",
    "                                           outline=self.color)\n",
    "          \n",
    "    def move_ball(self):\n",
    "        # 移动球的时候，需要控制球的方向            \n",
    "        # 每次移动后，球都有一个新的坐标\n",
    "        self.xpos += self.xvelocity\n",
    "        # 同理计算ypos\n",
    "        \n",
    "        # 以下判断是会否撞墙\n",
    "        # 撞了南墙就要回头\n",
    "        # 注意撞墙的算法判断\n",
    "        if self.xpos + self.radius >= self.scrnwidth:\n",
    "            # 装到了右边墙\n",
    "            self.xvelocity = -self.xvelocity\n",
    "            # 或者以下代码\n",
    "            # self.xvelocity *= -1\n",
    "        # 同理可以判断撞别的墙的算法\n",
    "                    \n",
    "        # 在画布上挪动图画\n",
    "        self.canvas.move(self.item, self.xvelocity, self.yvelocity)\n",
    "                    \n",
    "                    \n",
    "                    \n",
    "class ScreenSaver():\n",
    "    '''\n",
    "    定义屏保的类\n",
    "    可以被启动\n",
    "    '''\n",
    "    # 如何装随机产生的球？\n",
    "    balls = list()\n",
    "                    \n",
    "    def __init__(self):\n",
    "        # 每次启动球的数量随机\n",
    "        self.num_balls = random.randint(6, 20)\n",
    "\n",
    "        self.root = tkinter.Tk()\n",
    "        # 取消边框\n",
    "        self.root.overrideredirect(1)\n",
    "        \n",
    "        # 任何鼠标移动都需要取消\n",
    "        self.root.bind('<Motion>', self.myquit)\n",
    "        # 同理，按动任何键盘都需要退出屏保\n",
    "\n",
    "        #得到屏幕大小规格\n",
    "        w,h = self.root.winfo_screenwidth(), self.winfo_screenheight()\n",
    "           \n",
    "        #创建画布，包括画布的归属，规格\n",
    "        self.canvas = tkinter.Canvas(self.root, width=2, height=h)\n",
    "        self.canvas.pack()\n",
    "                    \n",
    "        # 在画布上画球\n",
    "        for i in range(self.num_balls):\n",
    "            ball = RandomBall(self.cavas, scrnwidth=w, scrnheight=h)\n",
    "            ball.create_ball()\n",
    "            self.balls.append(ball)\n",
    "                        \n",
    "        self.run_screen_saver()\n",
    "        self.root.mainloop()\n",
    "  \n",
    "    def run_screen_saver():\n",
    "        for ball in self.balls:\n",
    "            ball.move_ball()\n",
    "                    \n",
    "        # after是200毫秒后启动一个函数，需要启动的函数是第二个参数\n",
    "        self.cavas.after(200, self.run_screen_saver)\n",
    "                    \n",
    "    def myquit(self, e):\n",
    "        # 此处只是利用了事件处理机制\n",
    "        # 实际上并不关心事件的类型 \n",
    "        # 作业：\n",
    "        # 此屏保程序扩展成，一旦捕获事件，则判断屏保不退出\n",
    "        # 显示一个Button，Ｂｕｔｔｏｎ上显示事件类型，点击Ｂｕｔｔｏｎ后屏保\n",
    "        # 才退出\n",
    "        self.root.destroy()\n",
    "                    \n",
    "if __name__ == \"__main__\":\n",
    "    # 启动屏保\n",
    "    ScreenSaver()\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on function wm_overrideredirect in module tkinter:\n",
      "\n",
      "wm_overrideredirect(self, boolean=None)\n",
      "    Instruct the window manager to ignore this widget\n",
      "    if BOOLEAN is given with 1. Return the current value if None\n",
      "    is given.\n",
      "\n"
     ]
    }
   ],
   "source": [
    "import tkinter\n",
    "\n",
    "help(tkinter.Tk.overrideredirect)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
